/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "nweb_event_handler.h"

#include <cmath>
#include <vector>

#include "arkweb/build/features/features.h"
#include "build/build_config.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/trace_event/trace_event.h"
#include "cef/include/base/cef_logging.h"
#include "cef/include/base/cef_macros.h"
#include "cef/include/internal/cef_types.h"
#include "cef/include/internal/cef_types_wrappers.h"
#include "ui/events/keycodes/keyboard_code_conversion_x.h"
#include "ui/events/keycodes/keyboard_code_conversion_xkb.h"
#include "ui/events/keycodes/keysym_to_unicode.h"
#include "ui/events/event_constants.h"

#if BUILDFLAG(IS_ARKWEB_EXT)
#include "arkweb/ohos_nweb_ex/build/features/features.h"
#endif

namespace OHOS::NWeb {

// static
std::shared_ptr<NWebEventHandler> NWebEventHandler::Create() {
  auto event_handler = std::make_shared<NWebEventHandler>();
  if (event_handler == nullptr) {
    LOG(ERROR) << "fail to create NWebEventHandler instance";
    return nullptr;
  }
  return event_handler;
}

NWebEventHandler::NWebEventHandler() {
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  mmi_adapter_ =
      OHOS::NWeb::OhosAdapterHelper::GetInstance().CreateMMIAdapter();
  if (mmi_adapter_ == nullptr) {
    LOG(ERROR) << "display_manager_adapter is nullptr";
    return;
  }
#endif  // BUILDFLAG(ARKWEB_INPUT_EVENTS)
}

void NWebEventHandler::OnDestroy() {
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  if (mmi_id_ > 0 && mmi_adapter_ != nullptr) {
    mmi_adapter_->UnregisterMMIInputListener(mmi_id_);
  }
#endif  // BUILDFLAG(ARKWEB_INPUT_EVENTS)
  browser_ = nullptr;
}

void NWebEventHandler::SetBrowser(CefRefPtr<CefBrowser> browser) {
  browser_ = browser;
}

void NWebEventHandler::OnTouchPress(int32_t id,
                                    double x,
                                    double y,
                                    bool from_overlay) {
#if BUILDFLAG(ARKWEB_SLIDE) || BUILDFLAG(ARKWEB_PER_DFX)
  TRACE_EVENT0("input",
               "NWebEventHandler::OnTouchPress sliding response begin");
#endif
  CefTouchEvent touch_pressed;
  touch_pressed.type = CEF_TET_PRESSED;
  touch_pressed.pointer_type = CEF_POINTER_TYPE_TOUCH;
  touch_pressed.id = id;
  touch_pressed.x = x;
  touch_pressed.y = y;
  touch_pressed.modifiers = EVENTFLAG_NONE;
  touch_pressed.from_overlay = from_overlay;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEvent(touch_pressed);
  }
}

void NWebEventHandler::OnTouchMove(int32_t id,
                                   double x,
                                   double y,
                                   bool from_overlay) {
  LOG(DEBUG) << "NWebEventHandler::OnTouchMove id = " << id 
      << ", from_overlay = " << from_overlay;
  CefTouchEvent touch_move;
  touch_move.type = CEF_TET_MOVED;
  touch_move.pointer_type = CEF_POINTER_TYPE_TOUCH;
  touch_move.id = id;
  touch_move.x = x;
  touch_move.y = y;
  touch_move.modifiers = EVENTFLAG_NONE;
  touch_move.from_overlay = from_overlay;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEvent(touch_move);
  }
}

void NWebEventHandler::OnTouchMove(
    const std::vector<std::shared_ptr<NWebTouchPointInfo>>& touch_point_infos,
    bool from_overlay,
    float virtual_pixel_ratio) {
  std::vector<CefTouchEvent> event_list{};

  std::string touch_point_info_str{"NWebEventHandler::OnTouchMove"};
  for (const auto& touch_point : touch_point_infos) {
    CefTouchEvent touch_move;
    touch_move.type = CEF_TET_MOVED;
    touch_move.pointer_type = CEF_POINTER_TYPE_TOUCH;
    touch_move.id = touch_point->GetId();
    touch_move.x = touch_point->GetX() / virtual_pixel_ratio;
    touch_move.y = touch_point->GetY() / virtual_pixel_ratio;
    touch_move.modifiers = EVENTFLAG_NONE;
    touch_move.from_overlay = from_overlay;
    touch_point_info_str += " id = " + std::to_string(touch_point->GetId());
    event_list.emplace_back(touch_move);
  }
  LOG(DEBUG) << touch_point_info_str << ", from_overlay = " << from_overlay;

  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEventList(event_list);
  }
}

void NWebEventHandler::OnTouchRelease(int32_t id,
                                      double x,
                                      double y,
                                      bool from_overlay) {
  LOG(INFO) << "NWebEventHandler::OnTouchRelease";
  CefTouchEvent touch_end;
  touch_end.type = CEF_TET_RELEASED;
  touch_end.pointer_type = CEF_POINTER_TYPE_TOUCH;
  touch_end.id = id;
  touch_end.x = x;
  touch_end.y = y;
  touch_end.modifiers = EVENTFLAG_NONE;
  touch_end.from_overlay = from_overlay;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEvent(touch_end);
  }
}

cef_pointer_type_t NWebEventHandler::ConvertSourceToolToPointerType(
    SourceTool source_tool) {
  switch (source_tool) {
    case SourceTool::PEN:
    case SourceTool::BRUSH:
    case SourceTool::PENCIL:
    case SourceTool::AIRBRUSH:
      return CEF_POINTER_TYPE_PEN;
    case SourceTool::RUBBER:
      return CEF_POINTER_TYPE_ERASER;
    case SourceTool::FINGER:
      return CEF_POINTER_TYPE_TOUCH;
    case SourceTool::MOUSE:
      return CEF_POINTER_TYPE_MOUSE;
    default:
      return CEF_POINTER_TYPE_UNKNOWN;
  }
}

float NWebEventHandler::ConvertRollAngleToTwist(float raw_roll_angle) {
  float real_angle = (raw_roll_angle > 32767.0f) ?
                    (raw_roll_angle - 65536.0f) :
                    raw_roll_angle;
  real_angle = std::clamp(real_angle, -180.0f, 180.0f);
  return (real_angle < 0.0f) ? (real_angle + 360.0f) : real_angle;
}

void NWebEventHandler::OnStylusTouchPress(
    std::shared_ptr<NWebStylusTouchPointInfo> stylus_touch_point_info,
    bool from_overlay,
    float virtual_pixel_ratio) {
#if BUILDFLAG(ARKWEB_SLIDE) || BUILDFLAG(ARKWEB_PER_DFX)
  TRACE_EVENT0("input", "NWebEventHandler::OnStylusTouchPress begin");
#endif

  CefTouchEvent touch_pressed;
  touch_pressed.type = CEF_TET_PRESSED;
  touch_pressed.pointer_type =
      ConvertSourceToolToPointerType(stylus_touch_point_info->GetSourceTool());
  touch_pressed.id = stylus_touch_point_info->GetId();
  touch_pressed.x = stylus_touch_point_info->GetX() / virtual_pixel_ratio;
  touch_pressed.y = stylus_touch_point_info->GetY() / virtual_pixel_ratio;
  touch_pressed.tiltX = stylus_touch_point_info->GetTiltX();
  touch_pressed.tiltY = stylus_touch_point_info->GetTiltY();
  touch_pressed.radius_x =
      static_cast<float>(stylus_touch_point_info->GetWidth()) / 2.0f;
  touch_pressed.radius_y =
      static_cast<float>(stylus_touch_point_info->GetHeight()) / 2.0f;
  touch_pressed.rotation_angle =
      ConvertRollAngleToTwist(stylus_touch_point_info->GetRollAngle());
  touch_pressed.pressure = stylus_touch_point_info->GetForce();
  touch_pressed.modifiers = EVENTFLAG_NONE;
  touch_pressed.from_overlay = from_overlay;

  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEvent(touch_pressed);
  }
}

void NWebEventHandler::OnStylusTouchRelease(
    std::shared_ptr<NWebStylusTouchPointInfo> stylus_touch_point_info,
    bool from_overlay,
    float virtual_pixel_ratio) {
  LOG(INFO) << "NWebEventHandler::OnStylusTouchRelease";

  CefTouchEvent touch_end;
  touch_end.type = CEF_TET_RELEASED;
  touch_end.pointer_type =
      ConvertSourceToolToPointerType(stylus_touch_point_info->GetSourceTool());
  touch_end.id = stylus_touch_point_info->GetId();
  touch_end.x = stylus_touch_point_info->GetX() / virtual_pixel_ratio;
  touch_end.y = stylus_touch_point_info->GetY() / virtual_pixel_ratio;
  touch_end.tiltX = stylus_touch_point_info->GetTiltX();
  touch_end.tiltY = stylus_touch_point_info->GetTiltY();
  touch_end.radius_x =
      static_cast<float>(stylus_touch_point_info->GetWidth()) / 2.0f;
  touch_end.radius_y =
      static_cast<float>(stylus_touch_point_info->GetHeight()) / 2.0f;
  touch_end.rotation_angle =
      ConvertRollAngleToTwist(stylus_touch_point_info->GetRollAngle());
  touch_end.pressure = stylus_touch_point_info->GetForce();
  touch_end.modifiers = EVENTFLAG_NONE;
  touch_end.from_overlay = from_overlay;

  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEvent(touch_end);
  }
}

void NWebEventHandler::OnStylusTouchMove(
    const std::vector<std::shared_ptr<NWebStylusTouchPointInfo>>&
        stylus_touch_point_infos,
    bool from_overlay,
    float virtual_pixel_ratio) {
  std::vector<CefTouchEvent> event_list{};

  std::string touch_point_info_str{"NWebEventHandler::OnStylusTouchMove"};
  for (const auto& touch_point : stylus_touch_point_infos) {
    CefTouchEvent touch_move;
    touch_move.type = CEF_TET_MOVED;
    touch_move.pointer_type =
        ConvertSourceToolToPointerType(touch_point->GetSourceTool());
    touch_move.id = touch_point->GetId();
    touch_move.x = touch_point->GetX() / virtual_pixel_ratio;
    touch_move.y = touch_point->GetY() / virtual_pixel_ratio;
    touch_move.tiltX = touch_point->GetTiltX();
    touch_move.tiltY = touch_point->GetTiltY();
    touch_move.radius_x = static_cast<float>(touch_point->GetWidth()) / 2.0f;
    touch_move.radius_y = static_cast<float>(touch_point->GetHeight()) / 2.0f;
    touch_move.rotation_angle =
        ConvertRollAngleToTwist(touch_point->GetRollAngle());
    touch_move.pressure = touch_point->GetForce();
    touch_move.modifiers = EVENTFLAG_NONE;
    touch_move.from_overlay = from_overlay;
    touch_point_info_str += " id = " + std::to_string(touch_point->GetId());
    event_list.emplace_back(touch_move);
  }
  LOG(DEBUG) << touch_point_info_str << ", from_overlay = " << from_overlay;

  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEventList(event_list);
  }
}

void NWebEventHandler::OnTouchCancel() {
  LOG(INFO) << "NWebEventHandler::OnTouchCancel";
  CefTouchEvent touch_cancelled;
  touch_cancelled.type = CEF_TET_CANCELLED;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEvent(touch_cancelled);
  }
}

void NWebEventHandler::OnTouchCancelById(int32_t id,
                                         double x,
                                         double y,
                                         bool from_overlay) {
  (void)from_overlay;
  CefTouchEvent touch_cancelled;
  touch_cancelled.type = CEF_TET_CANCELLED;
  touch_cancelled.pointer_type = CEF_POINTER_TYPE_TOUCH;
  touch_cancelled.id = id;
  touch_cancelled.x = x;
  touch_cancelled.y = y;
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->SendTouchEvent(touch_cancelled);
  }
}

#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
void NWebEventHandler::SendKeyEventFromMMI(int32_t keyCode, int32_t keyAction) {
  if (!isFocus_ || !NWebInputDelegate::IsMMIKeyEvent(keyCode)) {
    return;
  }
  LOG(DEBUG) << "SendKeyEventFromMMI keyAction = " << keyAction;
  SendKeyEvent(keyCode, keyAction);
}

bool NWebEventHandler::SendKeyEventFromAce(int32_t keyCode, int32_t keyAction) {
  if (mmi_id_ >= 0 && NWebInputDelegate::IsMMIKeyEvent(keyCode)) {
    return true;
  }
  LOG(DEBUG) << "SendKeyEventFromAce keyAction = " << keyAction;
  return SendKeyEvent(keyCode, keyAction);
}

bool NWebEventHandler::WebSendKeyEventFromAce(
    int32_t keyCode,
    int32_t keyAction,
    const std::vector<int32_t>& pressedCodes) {
  if (mmi_id_ >= 0 && NWebInputDelegate::IsMMIKeyEvent(keyCode)) {
    return true;
  }
  LOG(DEBUG) << "WebSendKeyEventFromAce keyAction = " << keyAction;
  return WebSendKeyEvent(keyCode, keyAction, pressedCodes);
}

bool NWebEventHandler::CreateCefKeyEvent(CefKeyEvent& keyEvent,
                                         int32_t keyCode,
                                         int32_t keyAction,
                                         int32_t modifiers) {
  keyEvent.windows_key_code =
      NWebInputDelegate::CefConverter("keycode", keyCode);
  if (keyEvent.windows_key_code == -1) {
    LOG(ERROR) << "WebSendKeyEvent keyCode conversion failed";
    return false;
  }
  int type = NWebInputDelegate::CefConverter("keyaction", keyAction);
  if (type == -1) {
    LOG(ERROR) << "keyaction conversion failed, keyAction:" << keyAction;
    return false;
  }
  keyEvent.type = static_cast<cef_key_event_type_t>(type);
  keyEvent.modifiers = modifiers;
  LOG(DEBUG) << "WebSendKeyEvent modifiers = " << keyEvent.modifiers;
  keyEvent.is_system_key = false;
  keyEvent.native_key_code =
      NWebInputDelegate::CefConverter("keyscancode", keyCode);
  ui::KeyboardCode key_code =
      static_cast<ui::KeyboardCode>(keyEvent.windows_key_code);
  int keysym = ui::XKeysymForWindowsKeyCode(
    key_code, keyEvent.modifiers & EVENTFLAG_SHIFT_DOWN, keyEvent.modifiers & EVENTFLAG_CAPS_LOCK_ON);
  char16_t character = ui::GetUnicodeCharacterFromXKeySym(keysym);
  keyEvent.character = keyEvent.unmodified_character = character;
  return true;
}

void NWebEventHandler::SendCefKeyEvent(CefKeyEvent& keyEvent) {
  if (!browser_ || !browser_->GetHost()) {
    LOG(ERROR) << "SendCefKeyEvent browser_ or Host is nullptr, browser_: "
               << !browser_;
    return;
  }
  if (keyEvent.windows_key_code == ui::VKEY_RETURN) {
    // Do not forward return key release events if no press event was handled.
    if (keyEvent.type == KEYEVENT_KEYUP && !accept_return_character_) {
      return;
    }
    // Accept return key character events between press and release events.
    accept_return_character_ = keyEvent.type == KEYEVENT_RAWKEYDOWN;
  }
  browser_->GetHost()->SendKeyEvent(keyEvent);

  if (keyEvent.type == KEYEVENT_RAWKEYDOWN) {
#if !defined(COMPONENT_BUILD) // FIXME
    ui::KeyboardCode key_code =
      static_cast<ui::KeyboardCode>(keyEvent.windows_key_code);
    int keysym = ui::XKeysymForWindowsKeyCode(
      key_code, keyEvent.modifiers & EVENTFLAG_SHIFT_DOWN, keyEvent.modifiers & EVENTFLAG_CAPS_LOCK_ON);
    ui::DomKey dom_key = ui::XKeySymToDomKey(keysym, keyEvent.character);
    if (dom_key.IsCharacter()) {
      keyEvent.type = KEYEVENT_CHAR;
      browser_->GetHost()->SendKeyEvent(keyEvent);
    }
#else
    keyEvent.type = KEYEVENT_CHAR;
    browser_->GetHost()->SendKeyEvent(keyEvent);
#endif
  }
}

bool NWebEventHandler::WebSendKeyEvent(
    int32_t keyCode,
    int32_t keyAction,
    const std::vector<int32_t>& pressedCodes) {
  LOG(DEBUG) << "WebSendKeyEvent keyAction = " << keyAction;
  if (keyCode < 0) {
    LOG(ERROR) << "WebSendKeyEvent obtaining invalid keyCode";
    return false;
  }
  CefKeyEvent keyEvent;
  int32_t modifiers =
      NWebInputDelegate::GetWebModifiers(keyCode, keyAction, pressedCodes);
  input_delegate_.SetModifiers(keyCode, keyAction);
  if (!CreateCefKeyEvent(keyEvent, keyCode, keyAction, modifiers)) {
    LOG(ERROR) << "WebSendKeyEvent create key event failed";
    return false;
  }
  SendCefKeyEvent(keyEvent);
  return true;
}
void NWebEventHandler::SendCefMouseWheelEvent(double x,
                                              double y,
                                              double deltaX,
                                              double deltaY,
                                              int32_t modifiers,
                                              int32_t source) {
  CefMouseEvent mouseEvent;
  mouseEvent.x = x;
  mouseEvent.y = y;
  mouseEvent.modifiers = modifiers;
  mouseEvent.source = source;
  LOG(DEBUG) << "WebSendMouseWheelEvent modifiers = " << mouseEvent.modifiers;
  LOG(DEBUG) << "WebSendMouseWheelEventV2 source = " << mouseEvent.source;
  if (!browser_ || !browser_->GetHost()) {
    LOG(ERROR)
        << "SendCefMouseWheelEvent browser_ or Host is nullptr, browser_: "
        << !browser_;
    return;
  }

  double horizontalDelta;
  double verticalDelta;
  if (mmi_id_ > 0 && (mouseEvent.modifiers & EVENTFLAG_SHIFT_DOWN)) {
    horizontalDelta = deltaY * input_delegate_.GetMouseWheelRatio();
    verticalDelta = deltaX * input_delegate_.GetMouseWheelRatio();
  } else {
    horizontalDelta = deltaX * input_delegate_.GetMouseWheelRatio();
    verticalDelta = deltaY * input_delegate_.GetMouseWheelRatio();
  }
  browser_->GetHost()->SendMouseWheelEvent(mouseEvent, horizontalDelta,
                                           verticalDelta);
}

void NWebEventHandler::WebSendMouseWheelEvent(
    double x,
    double y,
    double deltaX,
    double deltaY,
    const std::vector<int32_t>& pressedCodes) {
  int32_t modifiers =
      NWebInputDelegate::GetWebModifiersByPressedCode(pressedCodes);
  SendCefMouseWheelEvent(x, y, deltaX, deltaY, modifiers);
}

bool NWebEventHandler::SendKeyboardEvent(
    const std::shared_ptr<OHOS::NWeb::NWebKeyboardEvent>& keyboardEvent) {
  if (!keyboardEvent) {
    return false;
  }
  if (keyboardEvent->GetKeyCode() < 0) {
    LOG(ERROR) << "SendKeyboardEvent obtaining invalid keyCode";
    return false;
  }
  CefKeyEvent keyEvent;
  int32_t modifiers = NWebInputDelegate::GetModifiersByKeyEvent(keyboardEvent);
  if (!CreateCefKeyEvent(keyEvent, keyboardEvent->GetKeyCode(),
                         keyboardEvent->GetAction(), modifiers)) {
    return false;
  }
  SendCefKeyEvent(keyEvent);
  return true;
}

#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
void NWebEventHandler::WebUpdateModifiers(
    CefMouseEvent& mouseInfo,
    const cef_mouse_button_type_t& buttonType) {
  if (NWebInputDelegate::IsMouseDown(previous_action_) &&
      previous_button_ != buttonType) {
    mouseInfo.modifiers |= NWebInputDelegate::GetMouseButtonModifiers(
        static_cast<cef_mouse_button_type_t>(previous_button_));
  }
}
#endif

void NWebEventHandler::WebSendMouseWheelEventV2(
    double x,
    double y,
    double deltaX,
    double deltaY,
    const std::vector<int32_t>& pressedCodes,
    int32_t source) {
  int32_t modifiers = NWebInputDelegate::GetWebModifiersByPressedCode(pressedCodes);
  SendCefMouseWheelEvent(x, y, deltaX, deltaY, modifiers, source);
}

void NWebEventHandler::WebSendTouchpadFlingEvent(
    double x,
    double y,
    double vx,
    double vy,
    const std::vector<int32_t>& pressedCodes) {
  CefMouseEvent mouseEvent;
  mouseEvent.x = x;
  mouseEvent.y = y;
  mouseEvent.modifiers =
      NWebInputDelegate::GetWebModifiersByPressedCode(pressedCodes);
  LOG(DEBUG) << "WebSendTouchpadFlingEvent modifiers = "
             << mouseEvent.modifiers;
  if (!browser_ || !browser_->GetHost()) {
    LOG(ERROR)
        << "WebSendTouchpadFlingEvent browser_ or host is nullptr, browser_: "
        << !browser_;
    return;
  }

  browser_->GetHost()->SendTouchpadFlingEvent(mouseEvent, vx, vy);
}

void NWebEventHandler::WebSendMouseEvent(
    const std::shared_ptr<OHOS::NWeb::NWebMouseEvent>& mouseEvent,
    float ratio) {
  if (!mouseEvent) {
    return;
  }
  CefMouseEvent mouseInfo;
  mouseInfo.x = mouseEvent->GetX() / ratio;
  mouseInfo.y = mouseEvent->GetY() / ratio;

// For PointerLock.
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  mouseInfo.raw_x = mouseEvent->GetRawX() / ratio;
  mouseInfo.raw_y = mouseEvent->GetRawY() / ratio;
#endif  // BUILDFLAG(ARKWEB_INPUT_EVENTS)
#if BUILDFLAG(ARKWEB_EX_TOPCONTROLS)
  if (browser_ && browser_->GetHost()) {
    mouseInfo.y -= browser_->GetHost()->GetShrinkViewportHeight();
  }
#endif
  cef_mouse_button_type_t buttonType = static_cast<cef_mouse_button_type_t>(
      NWebInputDelegate::CefConverter("mousebutton", mouseEvent->GetButton()));
  mouseInfo.modifiers = NWebInputDelegate::GetWebMouseModifiersByPressedCode(
      buttonType, mouseEvent->GetPressKeyCodes());
  LOG(DEBUG) << "WebSendMouseEvent modifiers: " << mouseInfo.modifiers;
  if (NWebInputDelegate::IsMouseLeave(mouseEvent->GetAction())) {
    is_in_web_ = false;
  } else if (NWebInputDelegate::IsMouseEnter(mouseEvent->GetAction())) {
    is_in_web_ = true;
  }
  if (browser_ && browser_->GetHost()) {
    if (NWebInputDelegate::IsMouseDown(mouseEvent->GetAction())) {
      previous_action_ = mouseEvent->GetAction();
      previous_button_ = buttonType;
      browser_->GetHost()->SendMouseClickEvent(mouseInfo, buttonType, false,
                                               mouseEvent->GetClickNum());
    } else if (NWebInputDelegate::IsMouseUp(mouseEvent->GetAction())) {
      previous_action_ = mouseEvent->GetAction();
      previous_button_ = buttonType;
      browser_->GetHost()->SendMouseClickEvent(mouseInfo, buttonType, true, 1);
      if (!is_in_web_) {
        browser_->GetHost()->SendMouseMoveEvent(mouseInfo, true);
      }
    } else if (NWebInputDelegate::IsMouseMove(mouseEvent->GetAction())) {
      if (mouseInfo.raw_x == 0 && mouseInfo.raw_y == 0) {
        LOG(DEBUG) << "no change in coordinates, cancel mouse move event";
        return;
      }

#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
      WebUpdateModifiers(mouseInfo, buttonType);
#endif
      last_mouse_x_ = mouseInfo.x;
      last_mouse_y_ = mouseInfo.y;
      browser_->GetHost()->SendMouseMoveEvent(mouseInfo, false);
    } else if (NWebInputDelegate::IsMouseLeave(mouseEvent->GetAction())) {
      browser_->GetHost()->SendMouseMoveEvent(mouseInfo, true);
    } else {
      previous_action_ = mouseEvent->GetAction();
      LOG(DEBUG) << "mouse event action: " << mouseEvent->GetAction();
    }
  }
}
#endif  // BUILDFLAG(ARKWEB_INPUT_EVENTS)

bool NWebEventHandler::SendKeyEvent(int32_t keyCode, int32_t keyAction) {
  LOG(DEBUG) << "SendKeyEvent keyAction = " << keyAction;
  if (keyCode < 0) {
    LOG(ERROR) << "SendKeyEvent obtaining invalid keyCode";
    return false;
  }
  CefKeyEvent keyEvent;
  input_delegate_.SetModifiers(keyCode, keyAction);
  int32_t modifiers = input_delegate_.GetModifiers();
  if (!CreateCefKeyEvent(keyEvent, keyCode, keyAction, modifiers)) {
    LOG(ERROR) << "SendKeyEvent create key event failed";
    return false;
  }
  SendCefKeyEvent(keyEvent);
  return true;
}

void NWebEventHandler::SendTouchpadFlingEvent(double x,
                                              double y,
                                              double vx,
                                              double vy) {
  CefMouseEvent mouseEvent;
  mouseEvent.x = x;
  mouseEvent.y = y;
  mouseEvent.modifiers = input_delegate_.GetModifiers();

#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  if (!browser_ || !browser_->GetHost()) {
    LOG(ERROR)
        << "SendTouchpadFlingEvent browser_ or host is nullptr, browser_: "
        << !browser_;
    return;
  }
  browser_->GetHost()->SendTouchpadFlingEvent(mouseEvent, vx, vy);
#endif  // BUILDFLAG(ARKWEB_INPUT_EVENTS)
}

void NWebEventHandler::SendMouseWheelEvent(double x,
                                           double y,
                                           double deltaX,
                                           double deltaY) {
#if BUILDFLAG(ARKWEB_INPUT_EVENTS)
  int32_t modifiers = input_delegate_.GetModifiers();
  SendCefMouseWheelEvent(x, y, deltaX, deltaY, modifiers);
#endif  // BUILDFLAG(ARKWEB_INPUT_EVENTS)
}

void NWebEventHandler::SendMouseEvent(int x,
                                      int y,
                                      int button,
                                      int action,
                                      int count) {
  CefMouseEvent mouseEvent;
  mouseEvent.x = x;
  mouseEvent.y = y;
#if BUILDFLAG(ARKWEB_EXT_TOPCONTROLS)
  if (browser_ && browser_->GetHost()) {
    mouseEvent.y -= browser_->GetHost()->GetShrinkViewportHeight();
  }
#endif
  cef_mouse_button_type_t buttonType = static_cast<cef_mouse_button_type_t>(
      NWebInputDelegate::CefConverter("mousebutton", button));
  mouseEvent.modifiers = input_delegate_.GetModifiers(buttonType);
  if (NWebInputDelegate::IsMouseLeave(action)) {
    is_in_web_ = false;
  } else if (NWebInputDelegate::IsMouseEnter(action)) {
    is_in_web_ = true;
  }
  if (browser_ && browser_->GetHost()) {
    if (NWebInputDelegate::IsMouseDown(action)) {
      previous_action_ = action;
      previous_button_ = buttonType;
#if BUILDFLAG(ARKWEB_CLIPBOARD)
      if (buttonType == MBT_LEFT) {
        browser_->GetHost()->SetFocus(true);
      }
#endif  // BUILDFLAG(ARKWEB_CLIPBOARD)
      browser_->GetHost()->SendMouseClickEvent(mouseEvent, buttonType, false,
                                               count);
    } else if (NWebInputDelegate::IsMouseUp(action)) {
      previous_action_ = action;
      previous_button_ = buttonType;
      browser_->GetHost()->SendMouseClickEvent(mouseEvent, buttonType, true, 1);
      if (!is_in_web_) {
        browser_->GetHost()->SendMouseMoveEvent(mouseEvent, true);
      }
    } else if (NWebInputDelegate::IsMouseMove(action)) {
      if (last_mouse_x_ == x && last_mouse_y_ == y) {
        LOG(INFO) << "no change in coordinates, cancel mouse move event";
        return;
      }

      last_mouse_x_ = x;
      last_mouse_y_ = y;
      browser_->GetHost()->SendMouseMoveEvent(mouseEvent, false);
    } else if (NWebInputDelegate::IsMouseLeave(action)) {
      if (NWebInputDelegate::IsMouseUp(previous_action_) ||
          previous_button_ == MBT_RIGHT) {
        browser_->GetHost()->SendMouseMoveEvent(mouseEvent, true);
      }
    } else {
      LOG(INFO) << "mouse event action: " << action;
    }
  }
}

void NWebEventHandler::NotifyForNextTouchEvent() {
  if (browser_ && browser_->GetHost()) {
    browser_->GetHost()->NotifyForNextTouchEvent();
  } else {
    LOG(ERROR)
        << "NotifyForNextTouchEvent browser_ or host is nullptr, browser_: "
        << !browser_;
  }
}
}  // namespace OHOS::NWeb
